<?php

class Encapsulator {
    var $data = array();
}

class ActionController {
    public $flash;
    public $before_filter = "";
    public $after_filter = "";
    public $cache_data;
    public $cache_use = false;
    /* change to true to use cache */
    public $rendered = false;
	public $tags = null;

    function __construct()
    {
        $this->flash = new flash();
    }

    function run_filter($controller, $filter, $content = "")
    {
        if (method_exists($controller . "Controller", $filter))
            return $this->$filter();
        else if (class_exists($filter) &&
                method_exists($filter, "filter")) {
            $c = new $filter();
            return $c->filter($this, $content);
        }
        return false;
    }

    function run_before_filters()
    {
        return $this->run_variable_filters("before");
    }

    function run_after_filters($content)
    {
        return $this->run_variable_filters("after", $content);
    }

    function run_variable_filters($filter, $content = "")
    {
        $filter .= "_filter";

        if (is_array($this->$filter) &&
                array_key_exists("filter", $this->$filter)) {
            $only = array();
            $except = array();
            while (list($key, $val) = each($this->$filter)) {
                switch ($key) {
                    case "only":
                        if (is_array($val)) {
                            foreach($val as $method)
                            $only[] = $method;
                        } else
                            $only[] = $val;
                        break;

                    case "except":
                        if (is_array($val)) {
                            foreach($val as $method)
                            $except[] = $method;
                        } else
                            $except[] = $val;
                        break;
                }
            }

            if (count($only) > 0 && array_search(Session::$action, $only) !== false) {
                $f = $this->$filter;
                return $this->run_filter(Session::$controller, $f["filter"], $content);
            } else if (count($except) > 0 && array_search(Session::$action, $except) === false) {
                $f = $this->$filter;
                return $this->run_filter(Session::$controller, $f["filter"], $content);
            }
        } else if (!empty($this->$filter))
            return $this->run_filter(Session::$controller, $this->$filter, $content);

        switch ($filter) {
            case "before_filter":
                return true;
                break;

            case "after_filter":
                return $this->parse_tags($content);
                break;
        }
    }

    function check_route()
    {
        $retval = Array();
        $r = explode("/", key($_GET));

        foreach(Cabbie::$routes as $route) {
            // router/search/:name
            // router/search/hisham
            $rt = $route["route"];

            if (count($rt) != count($r)) {
                continue;
            }

            /* case of an empty route matching an empty url */
            if (empty($rt[0]) && count($r) == 1) {
                $retval["route"] = $route;
                $retval["values"] = array();
                return $retval;
            }

            $j = count($rt);
            $ret = Array();
            /* empty it */
            for($i = 0; $i < $j; $i++) {
                if (!strstr($rt[$i], ':') && $rt[$i] != $r[$i]) {
                    break;
                } else if (strstr($rt[$i], ':')) {
                    $ret[$rt[$i]] = $r[$i];
                }
            }
            if (count($ret) > 0) {
                /* set any extra keywords provided by the route */
                foreach ($route['keywords'] as $key => $val)
                $ret[$key] = $val;

                $retval["route"] = $route;
                $retval["values"] = $ret;
                break;
            } ;
        }

        return $retval;
    }

    function process_route()
    {
        $controller = "";
        $action = "";
        $route = Array();
        $qs = array();

        if (array_key_exists("__mod_rewrite", $_GET))
            Session::$mod_rewrite = true;

        if (!empty($_GET['controller'])) {
            $controller = $_GET['controller'];
            $action = $_GET['action'];
        } else if (key($_GET) != "__mod_rewrite") {
            $where = explode("/", key($_GET));
            $controller = array_shift($where);
            if (count($where) > 0)
                $action = array_shift($where);
            foreach($where as $q)
            Session::$querystring[] = $q;
        }
        // $HOSTS= explode(".",$_SERVER["SERVER_NAME"]);
        // if (($HOSTS[0] != "dev") AND ($HOSTS[0] != "upino")) Session::$subdomain=$HOSTS[0];
        $route = $this->check_route();

        /* do we have a dir listing? */
        $dir = "$_SERVER[DOCUMENT_ROOT]/$controller/$action/" . join("/", Session::$querystring);
        if (is_dir($dir) && !empty($controller)) {
            foreach(Cabbie::$index_files as $index)
            if (file_exists("$dir/$index")) {
                include("$dir/$index");
                exit();
            }

            $this->render_dir($dir);
            $f = new flash();
            $f->cleanup();
            exit();
        }

        if (empty($controller))
            $controller = "index";

        if (empty($action))
            $action = "main";

        foreach ($_GET as $key => $value) {
            Cabbie::$params[$key] = $value;
        }

        if (count($route) > 0) {
            $controller = $route["route"]["controller"];
            $action = $route["route"]["action"];

            foreach ($route["values"] as $key => $value)
            Cabbie::$params[ltrim($key, ':')] = $value;
        }

        foreach ($_POST as $key => $value) {
            Cabbie::$params[$key] = $value;
        }

        Session::$controller = $controller;
        Session::$action = $action;

        if (class_exists($controller . "Controller")) {
            $controller_class = $controller . "Controller";
            $obj = new $controller_class();
            Session::$controller_object = $obj;
            Session::$controller_path = Cabbie::$url_prefix . "/" . Session::$controller;
            $obj->do_render($controller, $action);
        } else {
            Session::$controller_object = null;
            echo "No controller specified!!";
        }
    }

    function process_route_from_service($route, $locals = array())
    {
        if (!strstr($route, '/'))
            return false;

        $route = trim($route, '/');
        $words = explode('/', $route);

        if (count($words) < 1)
            return false;

        Session::$controller = $words[0];
        Session::$action = $words[1] ? $words[1] : "main";

        if (class_exists(Session::$controller . "Controller")) {
            $controller_class = Session::$controller . "Controller";
            $obj = new $controller_class();
            return $obj->do_render(Session::$controller, Session::$action, true, $locals);
        } else {
            echo "No controller specified!!";
            return false;
        }
    }

    /**
     * Take in an active controller object, look for all non-object tuples,
     * add them to array, look for all object values, if they are ActiveRecord
     * objects, recursively add their values to the array and prefix them with
     * the association's name
     */
    private function obj_to_array($tmp_key, $tmp_value, &$en, $cur_key = "")
    {
        if (!is_object($tmp_value)) {
            $en->data[$tmp_key] = $tmp_value;
            return;
        } else {
            $obj = new ReflectionClass($tmp_value);
            if ($obj->isSubclassOf(new ReflectionClass("ActiveRecord"))) {
                foreach ($tmp_value->content_columns as $col) {
                    $en->data[$cur_key . $col["name"]] = $tmp_value->$col["name"];
                }

                foreach ($tmp_value->has_one as $key => $value) {
                    $this->obj_to_array($key, $tmp_value->$key, $en, $cur_key . $key . "::");
                }
            }
        }
    }

    function do_render($controller, $action, $no_render = false, $locals = array(), $options = array())
    {
        if (file_exists(Cabbie::$approot . "/translations/" . Cabbie::$language . "/$controller" . "_translations.php") || $no_render == true) {
            include(Cabbie::$approot . "/translations/" . Cabbie::$language . "/$controller" . "_translations.php");
        } else {
            if (file_exists(Cabbie::$approot . "/translations/en/$controller" . "_translations.php") || $no_render == true) {
                include(Cabbie::$approot . "/translations/en/$controller" . "_translations.php");
            }
        }
        $this->tr = $trans;


        if (method_exists($controller . "Controller", $action)) {
            /* if the filter sets this to false, then we dont run the action */
            $filter_result = $this->run_before_filters();

            if ($filter_result) {
                /* We're doing this mainly for the weborb code that wants to emulate
         * a form being posted.
         */
                foreach ($locals as $key => $value)
                $_REQUEST[$key] = $value;

                $this->$action();

                if ($this->rendered == true)
                    return;

                ob_start();
                $options["template"] = "$controller/$action";
                $options["no_render"] = $no_render;

                $en = $this->render($options);
                $content = ob_get_clean();

                if ($en)
                    return $en;

                $content = $this->run_after_filters($content);

                if ($this->cache_view == true) {
                    $cache = new RemoteCache();
                    $cache->item_set($this->cache_key, $content, 300);
                }

                echo $this->parse_tags($content);
            }
        } else {
            if (class_exists($controller . "Controller") && method_exists($controller . "Controller", "action_not_found")) {
                $this->action_not_found();
            } else if (class_exists($controller . "Controller"))
                echo "Specified action does not exist!";
            else
                echo "No controller specified!!";
        }
    }

    function cache_exists($key)
    {
        $c = new LocalCache();
        $this->cache_data = $c->item_get($key);

        if ($this->cache_data === false) {
            unset($this->cache_data);
            return false;
        }

        return true;
    }

    /* TODO: finish this code */
    function render($options = array())
    {
        if (array_key_exists("component", $options) && $options["component"]) {
            return $this->render_component($options);
        }

        if (array_key_exists("layout", $options)) {
            if ($options["layout"] === false)
                $this->rendered = true;
        }

        if (array_key_exists("buffer", $options)) {
        	$this->buffer = true;
        }

        if (array_key_exists("template", $options)) {
            $template = explode("/", $options["template"]);
            if (count($template) != 2)
                return null;

            $controller = $template[0];
            $action = $template[1];
        	//log_message("contr: $controller , action: $action no_render:".var_dump($no_render));
            $no_render = array_key_exists("no_render", $options) ? $options["no_render"] : false;
            if (file_exists(Cabbie::$approot . "/views/$controller/$action.php") || $no_render == true) {
            	$controller_locals = get_object_vars($this);

                if (array_key_exists("locals", $options)) {
                    foreach ($options["locals"] as $k => $v)
                    $controller_locals[$k] = $v;
                }

                if (is_array($controller_locals)) {
                    /* we can combine the following two and move the if / else
           * statement inside, but then we compare more often
           */
                    if ($no_render == false) {
                        foreach ($controller_locals as $tmp_key => $tmp_value) {
                            $ {
                                $tmp_key} = $tmp_value;
                        }
                        unset($tmp_key);
                        unset($tmp_value);
                    }else {
                        $en = new Encapsulator();
                        foreach ($controller_locals as $tmp_key => $tmp_value) {
                            $this->obj_to_array($tmp_key, $tmp_value, $en);
                        }
                        unset($tmp_value);
                        return $en;
                    }
                }
                ob_start();

                /* check to see if we're fetching from the cache */
                if ($this->cache_use === true)
                    $c = new LocalCache();
                if ($this->cache_use === true && isset($this->cache_key) && (isset($this->cache_data) || ($cnt = $c->item_get($this->cache_key)) !== false)) {
                    if (isset($this->cache_data))
                        $content = $this->cache_data;
                    else
                        $content = $cnt;

                    unset($this->cache_data);
                    unset($this->cache_key);
                }else {
                    include(Cabbie::$approot . "/views/$controller/$action.php");
                    $content = ob_get_clean();
                    if ($this->cache_use === true)
                        $c->item_set($this->cache_key, $content);
					if ($this->buffer === true) {
						$this->buffer = false;
						$this->rendered = false;
						return $this->parse_tags($content);
					}
                }

                if (file_exists(Cabbie::$approot . "/views/layouts/${controller}_layout.php") && !$this->rendered) {
                    ob_start();
                    $content_for_layout = $content;
                    include(Cabbie::$approot . "/views/layouts/${controller}_layout.php");
                    $content = ob_get_clean();
                }else {
                    if (file_exists(Cabbie::$approot . "/views/layouts/" . Cabbie::$default_layout . ".php") && !$this->rendered) {
                        ob_start();
                        $content_for_layout = $this->parse_tags($content);
                        include(Cabbie::$approot . "/views/layouts/" . Cabbie::$default_layout . ".php");
                        $content = ob_get_clean();
                    }
                }

                if ($this->buffer == true) {
                	$this->buffer = false;
                	$this->rendered = false;
                    return $this->parse_tags($content);
                } else {
                    echo $this->parse_tags($content);
					return null;
                }
            }else {
                /* TODO: we probably should raise an exception here, return false */
                echo "WARNING: could not find requested template" .
                Cabbie::$approot . "/views/$controller/$action.php";
                return null;
            }
        }
    }

	function add_tag($tag,$data){
		$this->tags[$tag]=$data;
	}

	public function parse_tags($data){
		if($this->tags != null){
			foreach($this->tags as $tag_id => $tag){
				log_message("TAG: $tag_id => $tag");
				$data=str_replace("{".$tag_id."}",$tag,$data);
			}
		}
		return $data;

	}

    /* TODO: finish this code */
    function render_component($options = array())
    {
        if (!strstr($options["component"], '/'))
            return false;

        $route = trim($options["component"], '/');
        $words = explode('/', $route);

        if (count($words) < 1)
            return false;

        $controller = $words[0];
        $action = $words[1] ? $words[1] : "main";

        $options["component"] = null;

    	if (!array_key_exists("locals", $options))
            $options["locals"] = array();

        if (class_exists($controller . "Controller")) {
            $controller_class = $controller . "Controller";
            $obj = new $controller_class();
            return $obj->do_render($controller, $action, false, $options["locals"], $options);
        } else {
            echo "No controller specified!!";
            return false;
        }
    }

    function render_dir($dir)
    {
        if (!($fd = opendir($dir))) {
            /* TODO: raise error and call appropriate callback */
            echo "Could not open dir: $dir";
            exit();
        } while (substr($dir, - 1) == "/")
        $dir = rtrim($dir, "/ ");

        $dir_name = explode("/", $dir);
        if (!empty($dir_name[count($dir_name) - 1]))
            $dir_name = $dir_name[count($dir_name) - 1];
        else
            $dir_name = $dir_name[count($dir_name) - 2];

        $dirs = 0;
        $files = 0;
        $size_files = 0;
        $size_rec = recursive_directory_size($dir, false);

        while ($file = readdir($fd)) {
            if ($file == "." || $file == "..")
                continue;

            if (is_dir("$dir/$file"))
                $dirs++;
            else if (is_file("$dir/$file"))
                $files ++;
            $size_files += filesize("$dir/$file");
        }

        $size_files = sprintf("%.2f", $size_files);

        ob_start();
        rewind($fd);
        $file_list = array();
        while ($file = readdir($fd)) {
            if ($file == "." || $file == "..")
                continue;
            $file_list[] = $file;
        }

        if (isset($_GET["sort"])) {
            switch ($_GET["sort"]) {
                case "date":
                    array_sort_by_func($file_list, create_function('$element',
                            'return filemtime("' . $dir . '/$element");'));
                    break;

                case "size":
                    array_sort_by_func($file_list, create_function('$element',
                            'return filesize("' . $dir . '/$element");'));
                    break;
                case "filename":
                    array_sort_by_func($file_list, create_function('$element',
                            'return $element;'));
                    break;
            }
        }

        foreach($file_list as $file) {
            if (is_dir("$dir/$file")) {
                $icon = "directory";
                $file_items = 0;
                if (($ft = opendir("$dir/$file")))
                    while ($f = readdir($ft)) {
                    if ($f == "." || $f == "..")
                        continue;
                    $file_items++;
                }
                $file_url = "$file/";
            } else {
                $type = $this->file_type_get("$dir/$file");
                $icon = $type;
                $file_url = $file;
                $file_items = 1;
            }

            $file_size = sprintf("%.2f", filesize("$dir/$file") / 1024);
            $file_modtime = filemtime("$dir/$file");
            $file_name = $file;
            include(Cabbie::$approot . "/views/layouts/_indexer_item.php");
        }

        fclose($fd);

        $content_for_layout = ob_get_clean();

        include(Cabbie::$approot . "/views/layouts/indexer.php");
    }

    private function file_type_get($file)
    {
        switch (strrchr($file, ".")) {
            case ".mp3":
            case ".ogg":
            case ".wav":
                return "audio";
                break;

            case ".tar":
            case ".bz2":
            case ".gz":
            case ".zip":
                return "compressed";
                break;

            case ".ttf":
            case ".pcf":
                return "font";
                break;

            case ".png":
            case ".jpg":
            case ".jpeg":
            case ".gif":
            case ".bmp":
            case ".tiff":
            case ".svg":
                return "image";
                break;

            case ".doc":
            case ".txt":
            case ".rtf":
                return "office";
                break;

            case ".pdf":
                return "pdf";
                break;

            case ".c":
            case ".h":
                return "source_c";
                break;

            case ".cpp":
            case ".cxx":
                return "source_cpp";
                break;

            case ".avi":
            case ".mov":
            case ".mpg":
            case ".mpeg":
                return "video";
                break;

            case ".html":
            case ".css":
            case ".htm":
                return "web";
                break;

            case ".exe":
                return "windows";
                break;

            default:
                return "file";
                break;
        }
    }
}

function array_sort_by_func(&$arr, $func)
{
    $tmpArr = array();
    foreach ($arr as $k => &$e) {
        $tmpArr[] = array('f' => $func($e), 'k' => $k, 'e' => &$e);
    }
    sort($tmpArr);
    $arr = array();
    foreach($tmpArr as &$fke) {
        $arr[$fke['k']] = &$fke['e'];
    }
}

?>